home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung / Power-Programmierung (Tewi)(1994).iso / magazine / drdobbs / ddjcompr / compact / share.c < prev    next >
C/C++ Source or Header  |  1990-11-05  |  11KB  |  503 lines

  1. /**********************************************************************
  2.  *    ex:se ts=4 sw=4 ai:
  3.  *
  4.  *    Shared Memory Communication Library.
  5.  *
  6.  *    Author: Gene H. Olson
  7.  *            Smartware Consulting
  8.  *
  9.  *    This is FREE software in the PUBLIC DOMAIN.
  10.  *
  11.  *    "Everything FREE contains no guarantee."
  12.  *
  13.  ***********************************************************************
  14.  *
  15.  *    This library is designed to be a re-usable shared-library
  16.  *    package for high-speed communication between cooperating
  17.  *    tasks using System V shared memory.
  18.  *
  19.  *    Unfortunately the interface to system V shared libraries
  20.  *    is sufficiently awkward that it is difficult to create
  21.  *    a simple interface.  This was the best I could think of.
  22.  *
  23.  *    In this interface, a parent program creates a shared
  24.  *    memory segment for use with its children using the
  25.  *    share_create() function.  This function returns 4 pipe
  26.  *    file descriptors to be selectively passed to its children.
  27.  *
  28.  *    Two of the file descriptors (pfd[2]) are passed to the
  29.  *    producer process and two are passed to the consumer
  30.  *    process.  The share_open() function is then called
  31.  *    with the appropriate file descriptors, and shared
  32.  *    memory is mapped into processor space.
  33.  *
  34.  *    In passing file descriptors, the producer task should
  35.  *    always be passed pfd[0] and pfd[1], and cfd[1] must
  36.  *    be closed.  If SIGPIPE interrupts are desired when
  37.  *    sending a buffer to a terminated consumer process,
  38.  *    cfd[0] should also be closed;  otherwise it should be
  39.  *    left open and never accessed.  The consumer task
  40.  *    should receive cfd[0], cfd[1] and close pfd[1].
  41.  *    Usually pfd[0] is left open in the consumer task so
  42.  *    it won't get SIGPIPE interrupts when returning buffers
  43.  *    after the producer process terminates.
  44.  *
  45.  *    Both producer and consumer processes obtain a memory
  46.  *    buffer to pass to the other by calling share_get().
  47.  *    They pass the buffer to the opposite task by calling
  48.  *    share_put().  share_get() blocks as necessary to wait
  49.  *    for a buffer.   The only real difference between the
  50.  *    producer and consumer processes is that the entire
  51.  *    buffer list is initially queued to the producer.
  52.  *
  53.  *    When communication is complete, both producer and
  54.  *    consumer can either terminate, or call share_close()
  55.  *    to deallocate resources.  However at least one of
  56.  *    them must always call share_destroy() to deallocate
  57.  *    the shared memory segments.  Failure to do this will
  58.  *    leave the segments hanging out there chewing up
  59.  *    swap space.
  60.  *
  61.  *    Note that the SHARE object malloc()ed throughout these
  62.  *    operations is not generally free()ed.  This wastes a
  63.  *    bit of memory but simplifies caller programming.
  64.  *
  65.  *    In keeping with the current craze for object-oriented
  66.  *    programming and information-hiding, the SHARE object
  67.  *    which controls all of this is opaque to the calling
  68.  *    routines, and should be declared as type (char *).
  69.  *    How you reconcile this with lint is unresolved,
  70.  *    but at least this module lints clean.
  71.  *
  72.  *    The module smtest.c provides a simple example of how
  73.  *    to use the library.
  74.  **********************************************************************/
  75.  
  76. #ifndef lint
  77. static char sccs[] = "@(#)share.c    1.10 11/3/90" ;
  78. #endif
  79.  
  80. #include <sys/types.h>
  81. #include <sys/ipc.h>
  82. #include <sys/shm.h>
  83.  
  84. #include <assert.h>
  85. #include <stdio.h>
  86. #include <errno.h>
  87.  
  88. extern char *shmat() ;
  89. extern char *memset() ;
  90. extern void exit() ;
  91. extern int    errno ;
  92.  
  93. /*
  94.  *    Patch for Xenix which tests valid memory for reads & writes
  95.  *    off by 1 byte.
  96.  */
  97.  
  98. #ifdef M_XENIX
  99. #    define MKLUDGE    2
  100. #else
  101. #    define MKLUDGE    0
  102. #endif
  103.  
  104. /*
  105.  *    Shared memory and pipe item descriptors.
  106.  */
  107.  
  108. typedef struct sh_struct SHARE ;
  109.  
  110. #define SHARE_MAXID        16            /* Max shared memory ID's */
  111.  
  112. struct sh_struct
  113. {
  114.     int*    sh_rbuf ;                /* Pipe receive buffer */
  115.     int        sh_fd[2] ;                /* Pipe read file descriptors */
  116.     int        sh_nid ;                /* Number of allocated IDs */
  117.     int        sh_nbuf ;                /* Number of buffers allocated */
  118.     int        sh_nmap ;                /* Number of segments mapped */
  119.     int        sh_in ;                    /* Circular buffer in pointer */
  120.     int        sh_out ;                /* Circular buffer in pointer */
  121.     int        sh_bufsize ;            /* Buffer size */
  122.     int        sh_rin ;                /* Pipe buffer IN index */
  123.     int        sh_rout ;                /* Pipe buffer OUT index */
  124.     int        sh_id[SHARE_MAXID] ;    /* Shared memory ID */
  125.     char*    sh_base[SHARE_MAXID] ;    /* Shared memory base */
  126.     int        sh_len[SHARE_MAXID];    /* Shared segment length */
  127. } ;
  128.  
  129. #if lint
  130. #    define    malloc(x)    0
  131. #endif
  132.  
  133.  
  134. /**************************************************************
  135.  *    share_create - Create a shared memory entity in the parent
  136.  *                   of two tasks to share memory.
  137.  **************************************************************/
  138.  
  139. SHARE *
  140. share_create(pfd, cfd, bufsize, nbuf)
  141. int *pfd ;                        /* Producer file descriptors */
  142. int *cfd ;                        /* Consumer file descriptors */
  143. int bufsize ;                    /* Buffer size */
  144. int nbuf ;                        /* Number of buffers */
  145. {
  146.     register int id ;
  147.     register int i ;
  148.     register int n ;
  149.     register int s ;
  150.     register SHARE* sh ;
  151.     int size ;
  152.     int fd[4] ;
  153.  
  154.     if (nbuf > 500) return(0) ;
  155.  
  156.     sh = (SHARE *) malloc(sizeof(SHARE)) ;
  157.     if (sh == 0) return(0) ;
  158.  
  159.     (void) memset((char *)sh, 0, sizeof(SHARE)) ;
  160.  
  161.     sh->sh_fd[0] = sh->sh_fd[1] = -1 ;
  162.  
  163.     sh->sh_bufsize = bufsize ;
  164.  
  165.     /*
  166.      *    Allocate pipes for bi-directional communication
  167.      *    between the producer and consumer processes.
  168.      */
  169.  
  170.     if (pipe(fd) == -1) goto fail ;
  171.  
  172.     if (pipe(fd + 2) == -1)
  173.     {
  174.         (void) close(fd[0]) ;
  175.         (void) close(fd[1]) ;
  176.         goto fail ;
  177.     }
  178.  
  179.     pfd[0] = fd[0] ;
  180.     pfd[1] = fd[3] ;
  181.  
  182.     cfd[0] = fd[2] ;
  183.     cfd[1] = fd[1] ;
  184.  
  185.     /*
  186.      *    Allocate the required number of buffers in as many
  187.      *    segments as needed.
  188.      *
  189.      *    When the system will not provide us with a single
  190.      *    segment of the requested size, repeatedly request
  191.      *    a segment of the next lower power-of-two size.
  192.      */
  193.  
  194.     for (n = nbuf ; n ; n -= s)
  195.     {
  196.         s = n ;
  197.  
  198.         while (s > 0)
  199.         {
  200.             if ((id = shmget(IPC_PRIVATE, s * bufsize + MKLUDGE, 0600)) >= 0)
  201.             {
  202.                 sh->sh_id[sh->sh_nid] = id ;
  203.                 sh->sh_len[sh->sh_nid] = s ;
  204.                 sh->sh_nid++ ;
  205.                 break ;
  206.             }
  207.  
  208.             i = s * bufsize + MKLUDGE - 1 ;
  209.             while (i & (i-1)) i = i & (i-1) ;
  210.             s = (i - MKLUDGE) / bufsize ;
  211.         }
  212.  
  213.         if (s <= 0)
  214.         {
  215.             if (sh->sh_nid == 0) goto fail ;
  216.             break ;
  217.         }
  218.         
  219.         sh->sh_nbuf += s ;
  220.  
  221.         if (sh->sh_nid == SHARE_MAXID) break ;
  222.     }
  223.  
  224.     /*
  225.      *    Write out the shared memory descriptor
  226.      *    to both pipes.
  227.      */
  228.  
  229.     if (write(cfd[1], (char *)sh, sizeof(SHARE)) == -1)
  230.     {
  231.         errno = EIO ;
  232.         goto fail ;
  233.     }
  234.  
  235.     if (write(pfd[1], (char *)sh, sizeof(SHARE)) == -1)
  236.     {
  237.         errno = EIO ;
  238.         goto fail ;
  239.     }
  240.  
  241.     /*
  242.      *    Allocate all buffers to the producer process.
  243.      */
  244.  
  245.     size = 0 ;
  246.  
  247.     for (i = 0 ; i < sh->sh_nbuf ; i++)
  248.     {
  249.         if (write(cfd[1], (char *)&size, sizeof(int)) == -1) goto fail ;
  250.     }
  251.  
  252.     return(sh) ;
  253.  
  254. fail:
  255.     share_destroy(sh) ;
  256.     free((char*)sh) ;
  257.  
  258.     return(0) ;
  259. }
  260.  
  261.  
  262.  
  263. /*******************************************************
  264.  *    share_close - Close a SHARE descriptor.
  265.  *******************************************************/
  266.  
  267. share_close(sh)
  268. register SHARE *sh ;
  269. {
  270.     register int i ;
  271.  
  272.     if (sh->sh_rbuf)
  273.     {
  274.         free((char *) sh->sh_rbuf) ;
  275.         sh->sh_rbuf = 0 ;
  276.     }
  277.  
  278.     for (i = 0 ; i < sh->sh_nmap ; i++)
  279.     {
  280.         (void) shmdt(sh->sh_base[i]) ;
  281.     }
  282.  
  283.     sh->sh_nmap = 0 ;
  284.  
  285.     for (i = 0 ; i < 2 ; i++)
  286.     {
  287.         if (sh->sh_fd[i] != -1)
  288.         {
  289.             (void) close(sh->sh_fd[i]) ;
  290.             sh->sh_fd[i] = -1 ;
  291.         }
  292.     }
  293. }
  294.  
  295.  
  296.  
  297. /*******************************************************
  298.  *    share_destroy - Remove share descritors.
  299.  *******************************************************/
  300.  
  301. share_destroy(sh)
  302. register SHARE *sh ;                /* Shared memory descriptor */
  303. {
  304.     int i ;
  305.  
  306.     share_close(sh) ;
  307.  
  308.     for (i = 0 ; i < sh->sh_nid ; i++)
  309.     {
  310.         (void) shmctl(sh->sh_id[i], IPC_RMID, (struct shmid_ds *)0) ;
  311.     }
  312.  
  313.     sh->sh_nid = 0 ;
  314. }
  315.  
  316.  
  317.  
  318. /*****************************************************
  319.  *    share_open - Open shared memory descriptor.
  320.  *****************************************************/
  321.  
  322. SHARE *
  323. share_open(fd)
  324. int fd[2] ;
  325. {
  326.     register SHARE *sh ;
  327.     register char *p ;
  328.     register int i ;
  329.  
  330.     /*
  331.      *    Read in the Share structure from the read file
  332.      *    descriptor.
  333.      */
  334.  
  335.     sh = (SHARE *)malloc(sizeof(SHARE)) ;
  336.  
  337.     if (sh == 0) return(0) ;
  338.  
  339.     if (read(fd[0], (char *)sh, sizeof(SHARE)) != sizeof(SHARE))
  340.     {
  341.         free((char *)sh) ;
  342.         return(0) ;
  343.     }
  344.  
  345.     /*
  346.      *    Initialize the structure.
  347.      */
  348.  
  349.     sh->sh_fd[0] = fd[0] ;
  350.     sh->sh_fd[1] = fd[1] ;
  351.  
  352.     /*
  353.      *    Map in the shared memory segment.
  354.      */
  355.  
  356.     for (i = 0 ; i < sh->sh_nid ; i++)
  357.     {
  358.         p = shmat(sh->sh_id[i], (char *)0, 0) ;
  359.  
  360.         if (p == 0)
  361.         {
  362.             share_destroy(sh) ;
  363.             return(0) ;
  364.         }
  365.  
  366.         sh->sh_nmap++ ;
  367.         sh->sh_base[i] = p ;
  368.  
  369. #if defined(sun) && 0
  370.         (void) mlock(sh->sh_base[i], (unsigned)(sh->sh_bufsize*sh->sh_len[i])) ;
  371. #endif
  372.     }
  373.  
  374.     return(sh) ;
  375. }
  376.  
  377.  
  378.  
  379. /*******************************************************
  380.  *    share_get - Fetch next block from shared memory.
  381.  *******************************************************/
  382.  
  383. int
  384. share_get(sh, buf, n)
  385. register SHARE *sh ;
  386. char **buf ;
  387. int *n ;
  388. {
  389.     register int i ;
  390.     register int r ;
  391.     register int index ;
  392.  
  393.     /*
  394.      *    For efficiency (probably misguided) allocate
  395.      *    a buffer of 64 items.
  396.      */
  397.  
  398.     if (sh->sh_rbuf == 0)
  399.     {
  400.         sh->sh_rbuf = (int *)malloc(64 * sizeof(int)) ;
  401.         sh->sh_rin = 0 ;
  402.         sh->sh_rout = 0 ;
  403.     }
  404.  
  405.     /*
  406.      *    When the buffer is dry, read in another block
  407.      *    of data sizes.
  408.      */
  409.  
  410.     if (sh->sh_rin == sh->sh_rout)
  411.     {
  412.         r = read(sh->sh_fd[0], (char *) sh->sh_rbuf, 64 * sizeof(int)) ;
  413.  
  414.         if (r <= 0) return(r) ;
  415.  
  416.         assert((r % sizeof(int)) == 0) ;
  417.  
  418.         sh->sh_rin = 0 ;
  419.         sh->sh_rout = r / sizeof(int) ;
  420.     }
  421.  
  422.     /*
  423.      *    Get the next buffer pointer & count.
  424.      */
  425.  
  426.     *n = sh->sh_rbuf[sh->sh_rin++] ;
  427.  
  428. #if DEBUG >= 2
  429.     if (1)
  430.     {
  431.         char b[100] ;
  432.         sprintf(b, "GET      %5d   %6d\n", sh->sh_in, *n) ;
  433.         write(2, b, strlen(b)) ;
  434.     }
  435. #endif
  436.  
  437.     index = sh->sh_out ;
  438.  
  439.     for (i = 0 ;; i++)
  440.     {
  441.         assert(i < sh->sh_nid) ;
  442.         
  443.         if (index < sh->sh_len[i])
  444.         {
  445.             *buf = sh->sh_base[i] + sh->sh_bufsize * index ;
  446.             break ;
  447.         }
  448.  
  449.         index -= sh->sh_len[i] ;
  450.     }
  451.  
  452.     if (++sh->sh_out == sh->sh_nbuf) sh->sh_out = 0 ;
  453.  
  454.     return(1) ;
  455. }
  456.  
  457.  
  458.  
  459. /***************************************************************
  460.  *    share_put - Put a data buffer on the queue to the remote.
  461.  ***************************************************************/
  462.  
  463. int
  464. share_put(sh, buf, n)
  465. SHARE *sh ;                            /* Share pointer */
  466. char *buf ;                            /* Buffer pointer */
  467. int n ;                                /* Number of bytes */
  468. {
  469.     register int i ;
  470.     register int r ;
  471.     register int index ;
  472.  
  473.     index = sh->sh_in ;
  474.  
  475.     for (i = 0 ;; i++)
  476.     {
  477.         assert(i < sh->sh_nid) ;
  478.         
  479.         if (index < sh->sh_len[i])
  480.         {
  481.             assert(buf == sh->sh_base[i] + sh->sh_bufsize * index) ;
  482.             break ;
  483.         }
  484.  
  485.         index -= sh->sh_len[i] ;
  486.     }
  487.  
  488. #if DEBUG >= 2
  489.     if (1)
  490.     {
  491.         char b[100] ;
  492.         sprintf(b, "PUT %5d        %6d\n", sh->sh_in, n) ;
  493.         write(2, b, strlen(b)) ;
  494.     }
  495. #endif
  496.  
  497.     if (++sh->sh_in == sh->sh_nbuf) sh->sh_in = 0 ;
  498.  
  499.     r = write(sh->sh_fd[1], (char *)&n, sizeof(n)) ;
  500.  
  501.     return(r <= 0 ? r : 1) ;
  502. }
  503.